/*
** StockDB
**
** Copyright (C) 2012 - ? doublegsoft
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "storage.h"
#include "logging.h"

//
// stockdb_create_database
//
void
stockdb_create_database(const char *dir, const char *name)
{
  char fn[4096] = {'\0'};
  strncpy(fn, dir, strlen(dir));
  strncat(fn, "/", 1);
  strncat(fn, name, strlen(name));
  strncat(fn, DATAFILE_SUFFIX, strlen(DATAFILE_SUFFIX));
  FILE *f = fopen(fn, "w");
  stocks_t stocks;
  stocks.size = 0;
  stocks.first = 0;
  stocks.last = 0;
  fwrite(&stocks, sizeof(stocks_t), 1, f);
  fflush(f);
  fclose(f);
  // TODO: INDEX FILE
}

//
// stockdb_append_stock
//
void
stockdb_append_stock(const char *dir, const char *name, stock_t *stock)
{
  ulong offset, last;
  stocks_t *stocks = stockdb_read_stocks(dir, name, &offset);
  // keep the old last offset
  last = stocks->last;
  stocks->size++;
  if (stocks->first == 0)
  {
    stocks->first = offset;
  }
  stocks->last = offset;

  stockdb_replace_datafile(dir, name, 0, stocks, sizeof(stocks_t));

  // more are existing
  if (last != 0)
  {
    stock_t *found = stockdb_read_stock(dir, name, last);
    found->next = offset;
    // write back
    stockdb_replace_datafile(dir, name, last, found, sizeof(stock_t));
    free(found);
  }
  stockdb_append_datafile(dir, name, stock, sizeof(stock_t));
  free(stocks);
}

//
// stockdb_append_stock_daily
//
int
stockdb_append_stock_daily(const char *dir, const char *name, const char *code, stock_daily_t *daily)
{
  stockdb_log_info("append stock '%s' daily data time = %ld", code, daily->time);
  ulong offset = 0;
  stock_t *stock = stockdb_find_stock(dir, name, code, &offset);
  stockdb_log_info("found");
  if (!stock)
    return ERROR_NO_STOCK_FOUND;
  stock->size++;
  ulong last = stockdb_sizeof_datafile(dir, name);

  if (stock->last != 0)
  {
    stock_daily_t *found = stockdb_read_stock_daily(dir, name, stock->last);
    found->next = last;
    stockdb_replace_datafile(dir, name, stock->last, found, sizeof(stock_daily_t));
    free(found);
  }
  // if first is not set
  if (stock->first == 0)
  {
    stock->first = last;
  }
  stock->last = last;
  stockdb_replace_datafile(dir, name, offset, stock, sizeof(stock_t));
  free(stock);
  stockdb_append_datafile(dir, name, daily, sizeof(stock_daily_t));
  return ERROR_NO_ERROR;
}

//
// stockdb_open_datafile
//
FILE *
stockdb_open_datafile(const char *dir, const char *name, const char *mode)
{
  char fn[4096] = {'\0'};
  strncpy(fn, dir, strlen(dir));
  strncat(fn, "/", 1);
  strncat(fn, name, strlen(name));
  strncat(fn, DATAFILE_SUFFIX, strlen(DATAFILE_SUFFIX));
  FILE *ret = fopen(fn, mode);
  return ret;
}

//
// stockdb_close_datafile
//
void
stockdb_close_datafile(FILE *dbf)
{
  fclose(dbf);
}

//
// stockdb_replace_datafile
//
void
stockdb_replace_datafile(const char *dir, const char *name, ulong offset, void *data, size_t size)
{
  FILE *dbf = stockdb_open_datafile(dir, name, "r+");
  fseek(dbf, offset, SEEK_SET);
  fwrite(data, size, 1, dbf);
  fflush(dbf);
  stockdb_close_datafile(dbf);
}

//
// stockdb_append_datafile
//
void
stockdb_append_datafile(const char *dir, const char *name, void *data, size_t size)
{
  FILE *dbf = stockdb_open_datafile(dir, name, "a+");
  fwrite(data, size, 1, dbf);
  fflush(dbf);
  stockdb_close_datafile(dbf);
}

//
// stockdb_sizeof_datafile
//
ulong
stockdb_sizeof_datafile(const char *dir, const char *name)
{
  FILE *dbf = stockdb_open_datafile(dir, name, "rb");
  fseek(dbf, 0, SEEK_END);
  ulong ret = ftell(dbf);
  rewind(dbf);
  stockdb_close_datafile(dbf);
  return ret;
}

//
// stockdb_find_stock
//
stock_t *
stockdb_find_stock(const char *dir, const char *name, const char *code, ulong *offset)
{
  ulong size = 0;
  stocks_t *header = stockdb_read_stocks(dir, name, &size);
  if (!header)
    return NULL;
  stock_t *stock = NULL;
  ulong pos = header->first;
  free(header);
  while ((stock = stockdb_read_stock(dir, name, pos)))
  {
    if (strcmp(stock->code, code) == 0)
    {
      *offset = pos;
      return stock;
    }
    pos = stock->next;
    free(stock);
    if (pos >= size)
      break;
  }
  return NULL;
}

//
// stockdb_read_stocks
//
stocks_t *
stockdb_read_stocks(const char *dir, const char *name, ulong *size)
{
  FILE *dbf = stockdb_open_datafile(dir, name, "rb");
  fseek(dbf, 0, SEEK_END);
  *size = ftell(dbf);
  rewind(dbf);
  size_t s = sizeof(stocks_t);
  stocks_t *ret = malloc(s);
  fread(ret, s, 1, dbf);
  stockdb_close_datafile(dbf);
  return ret;
}

//
// stockdb_read_stock
//
stock_t *
stockdb_read_stock(const char *dir, const char *name, ulong offset)
{
  FILE *dbf = stockdb_open_datafile(dir, name, "rb");
  fseek(dbf, offset, SEEK_SET);
  size_t s = sizeof(stock_t);
  stock_t *ret = malloc(s);
  fread(ret, s, 1, dbf);
  stockdb_close_datafile(dbf);
  return ret;
}

//
// stockdb_read_stock_daily
//
stock_daily_t *
stockdb_read_stock_daily(const char *dir, const char *name, ulong offset)
{
  FILE *dbf = stockdb_open_datafile(dir, name, "rb");
  fseek(dbf, offset, SEEK_SET);
  size_t s = sizeof(stock_daily_t);
  stock_daily_t *ret = malloc(s);
  fread(ret, s, 1, dbf);
  stockdb_close_datafile(dbf);
  return ret;
}
